import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;


public class Main 
{

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException 
	{	
		String arquivoEntrada = "/home/leozinho/Área de Trabalho/computacao natural/knapsack/dset1n500M050.dat";
		String arquivoSaida = "estatisticas.txt";
		
		int tamPopulacao = 100, k = 2, nGeracoes = 100, nrExprimento = 1;
		double probCross = 0.95, probMutacao = 0.01;
		boolean elitismo = true, repeticao = true;
		
		for (int i = 0; i < args.length; i++)
		{
			if(args[i].equals("-t"))
				tamPopulacao = Integer.parseInt(args[i+1]);
			else if(args[i].equals("-g"))
				nGeracoes = Integer.parseInt(args[i+1]);
			else if(args[i].equals("-k"))
				k = Integer.parseInt(args[i+1]);
			else if(args[i].equals("-c"))
				probCross = Double.parseDouble(args[i+1]);
			else if(args[i].equals("-m"))
				probMutacao = Double.parseDouble(args[i+1]);
			else if(args[i].equals("-e"))
				elitismo = (Integer.parseInt(args[i+1]) == 1) ? true : false;
			else if(args[i].equals("-r"))
				repeticao = (Integer.parseInt(args[i+1]) == 1) ? true : false;
			else if(args[i].equals("-i"))
				arquivoEntrada = args[i+1];
			else if(args[i].equals("-o"))
				arquivoSaida = args[i+1];
			else if(args[i].equals("-x"))
				nrExprimento = Integer.parseInt(args[i+1]);
		}
		
		BufferedWriter out = new BufferedWriter(new FileWriter(arquivoSaida));

		int somaMelhorFitness = 0, somaPiorFitness = 0, somaNrIndRepetidos = 0, somaNrIndCrossMelhores = 0, somaNrIndCrossPiores = 0;
		double somaFitnessMedia = 0;
		
		// Estatísticas
		// Coluna 1: Fitness do melhor indivíduo
		// Coluna 2: Fitness do pior indivíduo
		// Coluna 3: Fitness média da população
		// Coluna 4: Número de indivíduos repetidos na população
		// Coluna 5: Numero de individuos gerados por crossover melhores que a fitness media dos pais
		// Coluna 6: Numero de individuos gerados por crossover piores que a fitness media dos pais
		double estatisticas[][] = new double[30][6];		
		double estatisticasPorGeracao[][] = new double[nGeracoes][6];
		
		for(int i = 0; i < 30; i++)
		{
			Knapsack ks = new Knapsack(arquivoEntrada);
			
			ks.solve(out, estatisticasPorGeracao, tamPopulacao, nGeracoes, k, probCross, probMutacao, elitismo, repeticao);
			
			// Estatisticas para a solucao final
			somaMelhorFitness += ks.getMelhorIndividuo().getFitness();
			somaPiorFitness += ks.getFitnessPiorIndividuo();
			somaFitnessMedia += ks.getFitnessMediaPopulacao();
			somaNrIndRepetidos += ks.getNumeroIndividuosRepetidos();
			somaNrIndCrossMelhores += ks.getNumeroIndCrossoverMelhores();
			somaNrIndCrossPiores += ks.getNumeroIndCrossoverPiores();
			
			estatisticas[i][0] = ks.getMelhorIndividuo().getFitness();
			estatisticas[i][1] = ks.getFitnessPiorIndividuo();
			estatisticas[i][2] = ks.getFitnessMediaPopulacao();
			estatisticas[i][3] = ks.getNumeroIndividuosRepetidos();
			estatisticas[i][4] = ks.getNumeroIndCrossoverMelhores();
			estatisticas[i][5] = ks.getNumeroIndCrossoverPiores();
		}
		
		calculaEimprimeEstatisticas(estatisticas, somaMelhorFitness, somaPiorFitness, somaFitnessMedia, 
									somaNrIndRepetidos, somaNrIndCrossMelhores, somaNrIndCrossPiores);
		
		// Estatisticas por geracao, para analise de convergencia da populacao
		imprimeEstatisticasPorGeracaoPlot(arquivoSaida, estatisticasPorGeracao, repeticao);
		
		// Estatisticas da solucao final
		imprimeEstatisticasPorExperimentoPlot(arquivoSaida, args, estatisticas, repeticao);
		
		out.close();
	}
	
	private static void calculaEimprimeEstatisticas(double estatisticas[][], int somaMelhorFitness, int somaPiorFitness, double somaFitnessMedia, 
											 int somaNrIndRepetidos, int somaNrIndCrossMelhores, int somaNrIndCrossPiores)
	{
		double mediaMelhorFitness, mediaPiorFitness, mediaFitnessMedia, mediaNrIndRepetidos, mediaNrIndCrossMelhores, mediaNrIndCrossPiores;
		double desvioMelhorFitness, desvioPiorFitness, desvioFitnessMedia, desvioNrIndRepetidos, desvioNrIndCrossMelhores, desvioNrIndCrossPiores;
		
		mediaMelhorFitness = somaMelhorFitness/30;
		mediaPiorFitness = somaPiorFitness/30;
		mediaFitnessMedia = somaFitnessMedia/30;
		mediaNrIndRepetidos = somaNrIndRepetidos/30;
		mediaNrIndCrossMelhores = somaNrIndCrossMelhores/30;
		mediaNrIndCrossPiores = somaNrIndCrossPiores/30;
		
		System.out.println("Média das melhores fitness: " + mediaMelhorFitness);
		System.out.println("Média das piores fitness: " + mediaPiorFitness);
		System.out.println("Média das fitness médias: " + mediaFitnessMedia);
		System.out.println("Média dos números de indivíduos repetidos: " + mediaNrIndRepetidos);
		System.out.println("Média dos números de indivíduos gerados por crossover que são melhores que os pais: " + mediaNrIndCrossMelhores);
		System.out.println("Média dos números de indivíduos gerados por crossover que são piores que os pais: " + mediaNrIndCrossPiores);
		
		somaMelhorFitness = 0;
		somaPiorFitness = 0;
		somaFitnessMedia = 0;
		somaNrIndRepetidos = 0;
		somaNrIndCrossMelhores = 0;
		somaNrIndCrossPiores = 0;
		
		for(int i = 0; i < 30; i++)
		{
			for(int j = 0; j < 6; j++)
			{
				if(j == 0)
					somaMelhorFitness += Math.pow((estatisticas[i][j] - mediaMelhorFitness), 2.0);
				if(j == 1)
					somaPiorFitness += Math.pow((estatisticas[i][j] - mediaPiorFitness), 2.0);
				if(j == 2)
					somaFitnessMedia += Math.pow((estatisticas[i][j] - mediaFitnessMedia), 2.0);
				if(j == 3)
					somaNrIndRepetidos += Math.pow((estatisticas[i][j] - mediaNrIndRepetidos), 2.0);
				if(j == 4)
					somaNrIndCrossMelhores += Math.pow((estatisticas[i][j] - mediaNrIndCrossMelhores), 2.0);
				if(j == 5)
					somaNrIndCrossPiores += Math.pow((estatisticas[i][j] - mediaNrIndCrossPiores), 2.0);
			}
		}
		
		desvioMelhorFitness = Math.sqrt((1.0/(30-1)) * somaMelhorFitness);
		desvioPiorFitness = Math.sqrt((1.0/(30-1)) * somaPiorFitness);
		desvioFitnessMedia = Math.sqrt((1.0/(30-1)) * somaFitnessMedia);
		desvioNrIndRepetidos = Math.sqrt((1.0/(30-1)) * somaNrIndRepetidos);
		desvioNrIndCrossMelhores = Math.sqrt((1.0/(30-1)) * somaNrIndCrossMelhores);
		desvioNrIndCrossPiores = Math.sqrt((1.0/(30-1)) * somaNrIndCrossPiores);
		
		System.out.println("Desvio padrão das melhores fitness: " + desvioMelhorFitness);
		System.out.println("Desvio padrão das piores fitness: " + desvioPiorFitness);
		System.out.println("Desvio padrão das fitness médias: " + desvioFitnessMedia);
		System.out.println("Desvio padrão dos números de indivíduos repetidos: " + desvioNrIndRepetidos);
		System.out.println("Desvio padrão dos números de indivíduos gerados por crossover que são melhores que os pais: " + desvioNrIndCrossMelhores);
		System.out.println("Desvio padrão dos números de indivíduos gerados por crossover que são piores que os pais: " + desvioNrIndCrossPiores);
	}
	
	private static void imprimeEstatisticasPorExperimentoPlot(String arquivoSaida, String args[], double[][] estatisticas, boolean repeticao) throws IOException
	{
		String nomeArquivo = "";
		double value = 0.0;
		
		// Agrupa as estatisticas ao variar tamamho da populacao, numero de geracoes e
		// operadores geneticos em aquivos separados
		if(arquivoSaida.contains("0p"))
		{
			for(int i = 0; i < args.length; i++)
			{
				if(args[i].equals("-t"))
					value = Double.parseDouble(args[i+1]);
			}
			nomeArquivo = arquivoSaida.replace(("0p" + value).replace(".0", ""), "0P");
		}
		else if(arquivoSaida.contains("0g"))
		{
			for(int i = 0; i < args.length; i++)
			{
				if(args[i].equals("-g"))
					value = Double.parseDouble(args[i+1]);
			}
			nomeArquivo = arquivoSaida.replace(("0g" + value).replace(".0", ""), "0G");
		}
		else if(arquivoSaida.contains("0m"))
		{
			for(int i = 0; i < args.length; i++)
			{
				if(args[i].equals("-m"))
					value = Double.parseDouble(args[i+1]);
			}
			nomeArquivo = arquivoSaida.replace(("0m" + value).replace(".", ""), "0M");
		}
		else if(arquivoSaida.contains("0c"))
		{
			for(int i = 0; i < args.length; i++)
			{
				if(args[i].equals("-c"))
					value = Double.parseDouble(args[i+1]);
			}
			nomeArquivo = arquivoSaida.replace(("0c" + value).replace(".", ""), "0C");
		}
		else return;
		
		// Arquivo no formato aceito pelo gnuplot
		FileOutputStream appendFile = new FileOutputStream("graficos/experimento/" + nomeArquivo.replace(".txt", "_PlotEstatisticas.txt"), true);
		
		int somaMelhorFitness = 0, somaPiorFitness = 0, somaNrIndRepetidos = 0, somaNrIndCrossMelhores = 0, somaNrIndCrossPiores = 0;
		double somaFitnessMedia = 0.0;
		
		for(int i = 0; i < 30; i++)
		{
			for(int j = 0; j < 6; j++)
			{
				if(j == 0)
					somaMelhorFitness += estatisticas[i][j];
				if(j == 1)
					somaPiorFitness += estatisticas[i][j];
				if(j == 2)
					somaFitnessMedia += estatisticas[i][j];
				if(j == 3 && repeticao)
					somaNrIndRepetidos += estatisticas[i][j];
				if(j == 4)
					somaNrIndCrossMelhores += estatisticas[i][j];
				if(j == 5)
					somaNrIndCrossPiores += estatisticas[i][j];
			}
		}
		
		String linha;
		linha = value + " " + (somaMelhorFitness/30);
		appendFile.write(linha.getBytes());
		linha =  " " + (somaPiorFitness/30);
		appendFile.write(linha.getBytes());
		linha = " " + (somaFitnessMedia/30);
		appendFile.write(linha.getBytes());
		linha = " " + (somaNrIndRepetidos/30);
		appendFile.write(linha.getBytes());
		linha = " " + (somaNrIndCrossMelhores/30);
		appendFile.write(linha.getBytes());
		linha = " " + (somaNrIndCrossPiores/30) + "\n";
		appendFile.write(linha.getBytes());
		
		appendFile.close();
	}
	
	private static void imprimeEstatisticasPorGeracaoPlot(String arquivoSaida, double[][] estatisticasPorGeracao, boolean repeticao) throws IOException
	{
		String nomeArquivoDados = "graficos/geracao/" + arquivoSaida.replace(".txt", "_PlotEstatisticasPorGeracao.txt");
		
		// Arquivos no formato aceito pelo gnuplot
		BufferedWriter arquivoDadosPlot = new BufferedWriter(new FileWriter(nomeArquivoDados));
		
		for(int i = 0; i < estatisticasPorGeracao.length; i++)
		{
			for(int j = 0; j < estatisticasPorGeracao[i].length; j++)
			{
				if(j == 0)
					arquivoDadosPlot.write(i + " " + (estatisticasPorGeracao[i][j]/30));
				if(j == 1)
					arquivoDadosPlot.write(" " + (estatisticasPorGeracao[i][j]/30));
				if(j == 2)
					arquivoDadosPlot.write(" " + (estatisticasPorGeracao[i][j]/30));
				if(j == 3 && repeticao)
					arquivoDadosPlot.write(" " + (estatisticasPorGeracao[i][j]/30));
				if(j == 4)
					arquivoDadosPlot.write(" " + (estatisticasPorGeracao[i][j]/30));
				if(j == 5)
					arquivoDadosPlot.write(" " + (estatisticasPorGeracao[i][j]/30) + "\n");
			}
		}
		
		arquivoDadosPlot.close();  
	}
}
